//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Linq;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.Research.ScientificWorkflow;
using System.Diagnostics;
using System.Threading;

namespace TridentBasicActivities.Activities
{
    [Name("ReplicatorActivity.", "Replicator")]
    [Description("Executes contained activities by creating specified number of replicated instances.")]
    [WorkflowCategory("Windows Workflow")]
    [Designer(typeof(SequentialActivityDesigner), typeof(IDesigner))]
    [ActivityValidator(typeof(ValidateReplicator))]
    public class Replicator : CompositeActivity, 
        IActivityEventListener<ActivityExecutionStatusChangedEventArgs>
    {
        #region Property Definitions
        public static DependencyProperty InitialChildDataProperty = DependencyProperty.Register("InitialChildData", typeof(IList), typeof(Replicator));

        [RequiredInputParam]
        [Name("InitialChildData")]
        [Description("Specify the collection which will contain initial child instance data fo rthe replicator.")]
        [BrowsableAttribute(true)]
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        public IList InitialChildData
        {
            get
            {
                return (IList)GetValue(Replicator.InitialChildDataProperty);
            }
            set
            {
                SetValue(Replicator.InitialChildDataProperty, value);
            }
        }

        public static DependencyProperty ExecutionTypeProperty = DependencyProperty.Register("ExecutionType", typeof(ExecutionType), typeof(Replicator));

        [RequiredInputParam]
        [Name("ExecutionType")]
        [Description("Specifies execution mode of the activity.")]
        [BrowsableAttribute(true)]
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        public ExecutionType ExecutionType
        {
            get
            {
                return (ExecutionType)GetValue(Replicator.ExecutionTypeProperty);
            }
            set
            {
                SetValue(Replicator.ExecutionTypeProperty, value);
            }
        }

        public static DependencyProperty CurrentItemProperty = DependencyProperty.Register("CurrentItem", typeof(object), typeof(Replicator));

        [OutputParam]
        [Name("Current Item")]
        [Description("This gives the current item of the replicator activity.")]
        [BrowsableAttribute(true)]
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        public object CurrentItem
        {
            get
            {
                return GetValue(Replicator.CurrentItemProperty);
            }
            set
            {
                SetValue(Replicator.CurrentItemProperty, value);
            }
        } 
        #endregion

        public Replicator()
		{
		}

        /// <summary>
        /// Overridden Execute method. This is used to execute all the children of the Replicator Activity.
        /// </summary>
        /// <param name="executionContext">Execution Context.</param>
        /// <returns>Execution Status.</returns>
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            // This is used to resolve the current assembly codebase.
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            // Always first child of the Wrapper Replicator container activity is ReplicatorActivity.
            ReplicatorActivity replicateChild = this.Activities[0] as ReplicatorActivity;

            if (null != replicateChild)
            {
                // Set the InitialData for the ReplicatorActivity.
                replicateChild.InitialChildData = this.InitialChildData;

                // Set the Execution type for the ReplicatorActivity.
                replicateChild.ExecutionType = this.ExecutionType;

                // Add an event handler for child Initialzed. 
                // This is used to get the current item which the replicator is working on.
                replicateChild.ChildInitialized += new EventHandler<ReplicatorChildEventArgs>(replicateChild_ChildInitialized);

                // Register for the Closed event . 
                // This will be used to Complete the execution of the ReplicatorActivity.
                replicateChild.RegisterForStatusChange(Activity.ClosedEvent, this);

                executionContext.ExecuteActivity(replicateChild);

                return ActivityExecutionStatus.Executing;
            }
            else
            {
                return ActivityExecutionStatus.Faulting;
            }
        }

        System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            System.Reflection.Assembly resolvedAssembly = null;

            //  If it is a type you know about return the assembly
            if (args.Name.Equals(this.GetType().Assembly.FullName))
            {
                resolvedAssembly = this.GetType().Assembly;
            }

            return resolvedAssembly;
        }

        void replicateChild_ChildInitialized(object sender, ReplicatorChildEventArgs e)
        {
            // Set the current Item property.
            this.CurrentItem = e.InstanceData;

            // Send the iteration started event.
            this.TrackData("IterationValue", this.CurrentItem);
        }

        #region IActivityEventListener<ActivityExecutionStatusChangedEventArgs> Members

        public void OnEvent(object sender, ActivityExecutionStatusChangedEventArgs e)
        {
            if (sender == null)
                throw new ArgumentNullException("sender");
            if (e == null)
                throw new ArgumentNullException("e");

            ActivityExecutionContext context = sender as ActivityExecutionContext;

            if (context == null)
                throw new ArgumentException("The sender must be an ActivityExecutionContext object", "sender");

            // As a good coding practice, unregister for the Closed event 
            // if the child activity is in the closed state.  
            if (e.Activity.ExecutionStatus == ActivityExecutionStatus.Closed)
            {
                e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);

                AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;

                context.CloseActivity();
            }
        }

        #endregion
    }
}
